<?php
/**
 * In this file the class '\UF\Drawing\Point' is defined.
 *
 * @category   UniKap-Framework
 * @package    Drawing
 * @subpackage Core
 * @author     Ulf -UniKado- Kadner <ulfikado@gmail.com>
 * @since      2014-04-04 16:17
 * @version    0.1
 */

namespace UF\Drawing
{

    /**
     * This class defines a 2 dimensional point.
     *
     * @since  v0.1
     */
    class Point implements \UF\IStringConverter, \UF\IEquatable, \Serializable
    {

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   F I E L D S   - - - - - - - - - - - - - - - - - - - - - -">

	/**
	 * The X coordinate (hor.)
	 *
	 * @var    int
         * @since  v0.1
	 */
        public $X;

        /**
         * The Y coordinate (vert.)
         *
         * @var    int
         * @since  v0.1
         */
        public $Y;

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - >   C O N S T R U C T O R   - - - - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Inits a new instance.
         *
         * @param  int  $x  The X coordinate (hor.)  (Defaults to 0)
         * @param  int  $y  The X coordinate (vert.) (Defaults to 0)
         * @since  v0.1
         */
        public function __construct( $x = 0, $y = 0 )
        {
            $this->X = $x;
            $this->Y = $y;
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - -">

        # <editor-fold defaultstate="collapsed" desc="Some checking methods">

        /**
         * Returns if X and Y is 0.
         *
         * @return boolean
         * @since  v0.1
         */
        public final function isEmpty()
        {
		return $this->X == 0 && $this->Y == 0;
	}

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="Overriding some 'magic' methods">

        /**
         * Returns the instance data string with format: <code>x=?; y=?</code>
         *
         * @return string
         * @since  v0.1
         */
        public function __toString()
        {
            return \sprintf( 'x=%d; y=%d', $this->X, $this->Y );
        }

        /**
         * Makes a Point cloneable.
         *
         * @return \UF\Drawing\Point
         * @since  v0.1
         */
        public function __clone()
        {
            return new \UF\Drawing\Point( $this->X, $this->Y );
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="XML-Methods">

        /**
         * Returns a string that contains all instance data as valid XML attributes. (e.g.:
         * <code>x="100" y="160"</code>)
         *
         * @return string
         * @since  v0.1
         */
        public final function toXMLParams()
        {
            return \sprintf( 'x="%d" y="%d"', $this->X, $this->Y );
        }

        /**
         * Writes the 2 XML attributes x and y to defined XmlWriter. So current a XML element must be started
         * and not closed by writer!
         *
         * @param \XMLWriter $w
         * @since  v0.1
         */
        public final function writeXMLAttributes( \XMLWriter $w )
        {
            $w->writeAttribute( 'x', $this->X );
            $w->writeAttribute( 'y', $this->Y );
        }

        /**
         * Writes a XML element with the defined name and the attributes 'x' and 'y'.
         *
         * @param \XMLWriter $w
         * @param  string    $elementName The XML element name. (Defaults to 'Point')
         * @since  v0.1
         */
        public final function writeXML( \XMLWriter $w, $elementName='Point' )
        {
            $w->startElement( $elementName );
            $this->writeXMLAttributes( $w );
            $w->endElement();
        }

        /**
         * Reads the data from the defined XML element (attributes 'x' + 'y' required) and on success
         * the current instance data will be overwritten by new data.
         *
         * @param \SimpleXMLElement $element
         * @return boolean
         * @since  v0.1
         */
        public final function readXML( \SimpleXMLElement $element )
        {
            if ( ! isset( $element['x'] ) || ! isset( $element['y'] ) ) return false;
            $this->X = \intval( (string)$element['x'] );
            $this->Y = \intval( (string)$element['y'] );
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="Implement \UF\IStringConverter">

        /**
         * Returns the instance data string with format: <code>x=?; y=?</code>
         *
         * @return string
         * @since  v0.1
         */
        public function toString()
        {
            return (string) $this;
        }

        /**
         * Reads the data from the defined string (for usable format see {@see \UF\Drawing\Point\toString()}) and
         * on success the current instance data will be overwritten by new data.
         *
         * @param  string $objectString
         * @return boolean
         * @since  v0.1
         */
        public function fromString( $objectString )
        {
            $hits = null;
            if ( !\preg_match( '~^x=(\d{1,4});\s*y=(\d{1,4})$~', $objectString, $hits ) ) return false;
            $this->X = \intval( $hits[1] );
            $this->Y = \intval( $hits[2] );
            return true;
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="Implement Serializable">

        /**
         * Returns the instance data, serialized to a string.
         *
         * @return string
         * @since  v0.1
         */
        public function serialize()
        {
            return \serialize( $this->toArray() );
        }

        /**
         * Constructs the object from serialized data.
         *
	 * @param  string $serialized The serialized string representation of the object.
         * @since  v0.1
         */
        public function unserialize( $serialized )
        {
            $this->fromArray( \unserialize( $serialized ) );
        }

        # </editor-fold>

        /**
         * Returns the X and the Y as a associative array with the keys
         * <code>'x' and 'y'</code>
         *
         * @return array Array with Keys <code>'x' and 'y'</code>
         * @since  v0.1
         */
        public function toArray()
        {
            return array(
                'x' => $this->X,
                'y' => $this->Y );
        }

        /**
         * Reads the data from the defined array (keys 'x' + 'y' required) and on success
         * the current instance data will be overwritten by new data.
         *
         * @param array $objectData Array with the keys 'x' and 'y'.
         * @return boolean
         * @since  v0.1
         */
        public function fromArray( array $objectData )
        {
            $objectData = \array_change_key_case( $objectData, \CASE_LOWER );
            if ( isset( $objectData['x'] ) && isset( $objectData['y'] ) )
            {
                $this->X = (int) $objectData['x'];
                $this->Y = (int) $objectData['y'];
                return true;
            }
            return false;
        }

        # <editor-fold defaultstate="collapsed" desc="Implement \UF\IEquatable">

        /**
         * Checks if the current instance is equal to defined value.
         *
         * If the 2nd parameter is TRUE, $value can only be equal if its a instance of {@see \UF\Drawing\Point}.
         * Otherwise also values are checked, that can be converted to a {@see \UF\Drawing\Point}, with
         * {@see \UF\Drawing\Point::TryParse()}.
         *
         * @param  \UF\Drawing\Point|int|array|string $value
         * @param  boolean $strict Allow only values of type {@see \UF\Drawing\Point}? (Defaults to FALSE)
         * @return boolean
         * @since  v0.1
         */
        public function equals( $value, $strict=false )
        {
            if ( \is_null( $value ) ) return false;
            if ( $value instanceof \UF\Drawing\Point ) return $value->X == $this->X && $value->Y == $this->Y;
            if ( $strict ) return false;
            $point = null;
            if ( !self::TryParse( $value, $point ) ) return false;
            return $point->X == $this->X && $point->Y == $this->Y;
        }

        # </editor-fold>

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   S T A T I C   M E T H O D S   - - - - - - - - - - - - - -">

        /**
         * Extracts from the passed value a {@ see \UF\Drawing\Point} instance, an returns it with the $output parameter
         * if the method self returns TRUE.
         *
         * @param  \UF\Drawing\Point|\UF\Drawing\Rectangle|string|array|\SimpleXMLElement $value The value to parse.
         * @param  \UF\Drawing\Point &$output Resulting {@see \UF\Drawing\Point} instance if method returns TRUE.
         * @return bool
         * @since  v0.1
         */
        public static function TryParse( $value, &$output )
        {
            if ( \is_null( $value ) ) return false;
            if ( $value instanceof \UF\Drawing\Point ) {
                $output = $value; return true; }
            if ( $value instanceof \UF\Drawing\Rectangle )  {
                $output = $value->Point; return true; }
            if ( \is_string( $value ) ) {
                $tmp = new \UF\Drawing\Point();
                if ( ! $tmp->fromString( $value ) ) return false;
                $output = $tmp;
                return true; }
            if ( \is_array( $value ) ) {
                $tmp = new \UF\Drawing\Point();
                if ( ! $tmp->fromArray( $value ) ) return false;
                $output = $tmp;
                return true; }
            if ( $value instanceof \SimpleXMLElement ) {
                $tmp = new \UF\Drawing\Point();
                if ( ! $tmp->readXML( $value ) ) return false;
                $output = $tmp;
                return true; }
            $type = new \UF\Type( $value );
            if ( $type->hasAssociatedString() ) {
                $tmp = new \UF\Drawing\Point();
                if ( ! $tmp->fromString( $type->getStringValue() ) ) return false;
                $output = $tmp;
                return true; }
            return false;
        }

        /**
         * Parse a string that contains some valid XML element attributes to a new {@see \UF\Drawing\Point} instance.
         *
         * @param  string $paramsStr XML-Attributes (e.g.: 'x="100" y="50"')
         * @return \UF\Drawing\Point Or boolean FALSE
         * @since  v0.1
         */
        public static function ParseXmlParams( $paramsStr )
        {
            $res = \UF\ArrayHelper::ParseAttributes( $paramsStr, true );
            if ( empty( $res ) || ! isset( $res['x'] ) || ! isset( $res['y'] ) ) return false;
            $x = \strval( \intval( $res['x']  ) );
            $y = \strval( \intval( $res['y'] ) );
            if ( $x != $res['x'] || $y != $res['y'] ) return false;
            return new \UF\Drawing\Point( \intval( $x ), \intval( $y ) );
        }

        /**
         * Parse a XML element to a new {@see \UF\Drawing\Point} instance.
         *
         * @param  \SimpleXMLElement $xml
         * @return \UF\Drawing\Point Or boolean FALSE
         * @since  v0.1
         */
        public static function ParseXml( \SimpleXMLElement $xml )
        {
            if ( ! isset( $xml['x'] ) || ! isset( $xml['y'] ) ) return false;
            return new \UF\Drawing\Point( \intval( (string)$xml['x'] ), \intval( (string)$xml['y'] ) );
        }

        /**
         * Parses a string with format <code>x=?; y=?</code> to a new {@see \UF\Drawing\Point} instance.
         *
         * @param  string $str
         * @return \UF\Drawing\Point Or boolean FALSE
         * @since  v0.1
         */
        public static function Parse( $str )
        {
            $res = new \UF\Drawing\Point();
            if ( ! $res->fromString( $str ) ) return false;
            if ( $res->isEmpty() )            return false;
            return $res;
        }

        # </editor-fold>

    }

}

